#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/* TODO:
     Add all the extra tests (fun... Woo...)
     Callback Tests (focus on indirect recursion)
     Mixed Tests
       Text and Binary files

     Old performance dirvers used non defined input files (typically Parser.c)
       to judge performance. This is annoying since there is no standard way
       to compaire performance, without building the old parser(s) to read the
       new test file.
       Generate and define a test file to judge general perormance, or
       seperate this out into it's own driver. IE, this file only checks
       for bugs, and does not care about performance.
 */

#include "Parser.h"

/* All tests are self contained. All tests must generate a file to load into  *
 *  the Parser via ParserInit() and LoadFile(). The exact same data must be   *
 *  loaded into the Parser vis LoadMemory(). All generated files must be      *
 *  removed before the program exits.                                         */


/* Acutal Tests */
  /* The following test operate on the Parser without any options enabled.    */
static void NoInitTest(void);
static void SimpleFragTest(void);
static void SeekPeekGrabTest(void);
static void NumberTest(void);
static void BinaryNumberTest(void);
static void TokenSetTest(void);

  /* Wildcard specific tests */
static void WildcardTest(void);

  /* Callback tests */

  /* Parser Performance Tests */

/* Helper Functions */
static int  GenerateFile  (const char *file, const char *data);
static void RemoveFile    (const char *file);
static int  TestGrabTokenL(const char *str, int linenum);
static int  TestPeekTokenL(const char *str, int linenum);
static int  ShouldBeEmptyL(int linenum);
static int  ShouldBeEOFL  (int linenum);
static int  SeekValidL    (const char *str, int linenum);
static int  SeekInvalidL  (const char *str, int linenum);
static int  TestGrabIntL  (int   val, int linenum);
static int  TestGrabFloatL(float val, int linenum);
static int  TestGrabCharL (char  val, int linenum);
static int  TestGrabBinaryIntL  (int   val, int linenum);
static int  TestGrabBinaryFloatL(float val, int linenum);
static int  PushTokenSep(const char *sep);
static int  PushTokenSeparator(const char *sep, int, int, int, void *, void *);

  /* Inject the line numbers, so I know where the error was generated.        */
#define TestGrabToken(a)       TestGrabTokenL(a, __LINE__);
#define TestPeekToken(a)       TestPeekTokenL(a, __LINE__);
#define ShouldBeEmpty()        ShouldBeEmptyL(__LINE__);
#define ShouldBeEOF()          ShouldBeEOFL(__LINE__);
#define SeekValid(a)           SeekValidL(a, __LINE__);
#define SeekInvalid(a)         SeekInvalidL(a, __LINE__);
#define TestGrabInt(a)         TestGrabIntL(a, __LINE__);
#define TestGrabFloat(a)       TestGrabFloatL(a, __LINE__);
#define TestGrabBinaryInt(a)   TestGrabBinaryIntL(a, __LINE__);
#define TestGrabBinaryFloat(a) TestGrabBinaryFloatL(a, __LINE__);
#define TestGrabChar(a)        TestGrabCharL(a, __LINE__);

#define AddTokenSep(a)         PushTokenSep(a)
#define AddTokenSeparator(a,b,c,d,e,f) PushTokenSeparator(a,b,c,d,e,f)

#define USE_HASH   if(UseHash) ParserEnable(PARSER_HASH);
#define CASE_INS   if(CaseIns) ParserEnable(PARSER_CASE_INSENSITIVE);
#define USE_WILD   if(UseWild) ParserEnable(PARSER_WILDCARD);
#define CLOSE_FILE if(CloseF)  ParserEnable(PARSER_CLOSE_FILE);

#define FORCE_WILD {UseWild = 0; ParserEnable(PARSER_WILDCARD);}

#define OPTIONS USE_HASH CASE_INS USE_WILD CLOSE_FILE

static int Verbose = 0;
static int UseHash = 0;
static int CaseIns = 0;
static int UseWild = 0;
static int CloseF  = 0;

void RunNewTests(void)
{
  int i;

  printf("\n\n---------------- New    Tests ----------------\n");

  for(i = 0; i <= ((1 << 0) | (1 << 1) | (1 << 2) | (1 << 3)); ++i)
  {
    UseHash = i & (1 << 0);
    CaseIns = i & (1 << 1);
    UseWild = i & (1 << 2);
    CloseF  = i & (1 << 3);
    printf("-------------");
    printf("Enabled: ");
    if(UseHash) printf("Hash   "); else printf("       ");
    if(CaseIns) printf("CaseInsensitive   "); else printf("                  ");
    if(UseWild) printf("Wildcards    "); else printf("             ");
    if(CloseF)  printf("Close File   "); else printf("             ");
    printf("\n");

    NoInitTest();
    SimpleFragTest();
    SeekPeekGrabTest();
    NumberTest();
    BinaryNumberTest();
    TokenSetTest();
    WildcardTest(); // Wildcards are forced in the test
  }
}


/*******************************************************************************
   Function: NoInitTest()

Description: Checks to see if any public Parser functions cause a crash if
               the parser was not initilized before calling them. IE, if
               if(!P ...) is not done first.
*******************************************************************************/
void NoInitTest(void)
{
  int errors = 0;
  printf("|--Not Init Test--|\n");

  if(ParserIsEnabled(0xFFF)){printf("  Error: Nothing has options set\n");      ++errors;}
  if(LoadMemory("asdf"))    {printf("  Error: Load Memory suceeded\n");         ++errors;}
  if(LoadFile("Parser.cpp")){printf("  Error: Load File suceeded\n");           ++errors;}
  if(AddTokenSet() != -1)   {printf("  Error: Add Token Set suceeded\n");       ++errors;}
  if(SetTokenSet(0) != -1)  {printf("  Error: Set Token Set suceeded\n");       ++errors;}
  if(GetTokenSet()  != -1)  {printf("  Error: Get Token Set suceeded\n");       ++errors;}

  if(!AddTokenSeparator("asdf", 0, -1, 0, 0, 0))
                            {printf("  Error: Add Token Separator suceeded\n"); ++errors;}
  if(!AddTokenSep("asdf"))  {printf("  Error: Add Token Sep suceeded\n");       ++errors;}
  if(GrabToken())           {printf("  Error: Grab Token Suceeded\n");          ++errors;}
  if(PeekToken())           {printf("  Error: Peek Token suceeded\n");          ++errors;}
  if(GrabInt())             {printf("  Error: Grab Int suceeded\n");            ++errors;}
  if(GrabBinaryInt())       {printf("  Error: Grab Binary Int Suceeded\n");     ++errors;}
  if(GrabFloat())           {printf("  Error: Grab Float suceeded\n");          ++errors;}
  if(GrabBinaryFloat())     {printf("  Error: Grab Binary Float suceeded\n");   ++errors;}
  if(GrabByte())            {printf("  Error: Grab Char suceeded\n");           ++errors;}
  if(GrabBytes(1))          {printf("  Error: Grab Bytes suceeded\n");          ++errors;}
  if(Seek("asdf"))          {printf("  Error: Seek suceeded\n");                ++errors;}
  if(SetFilePosition(10))   {printf("  Error: Set File Position suceeded\n");   ++errors;}
  if(GetFilePosition())     {printf("  Error: Get File Position suceeded\n");   ++errors;}
  if(ErrorCode() != PARSER_NOT_INITIALIZED) {PrintErrorCode();                  ++errors;}
  if(!End())                {printf("  Error: Not the end of nothing.\n");      ++errors;}
  if(ParserMemoryUsage())   {printf("  Error: Parser using fictional memory\n");++errors;}
  ParserDeInit();
  ParserEnable(0xFFFFF);
  ParserDisable(0xFFFFF);

  if(errors)
    printf("  Failed: %d errors occured\n", errors);
  else
    printf("  Pass\n");

  printf("\n");
}


/*******************************************************************************
   Function: SimpleFragTest()

Description: Checks to make sure the Parser correctly deals with buffering
               the file. This funciton focus on causing border conditions
               where chunks of the next token fall partly in to the buffer,
               and the rest is in the file.
             Checks to make sure the entire file can be read into a returned
               token.
             Checks to make sure the entire file can be discarded.
             Checks to make sure LoadMemory() and LoadFile can be used
               together.
*******************************************************************************/
#define SFT_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define SFT_F "SimpleFragTest.txt"
#define SFT_D "0123456789abcdef"
#define SFT_LOAD_DATA                                                    \
switch(input)                                                            \
{                                                                        \
  case 0: ParserInit(SFT_F, i);                                   break; \
  case 1: ParserInit(0, i);   LoadFile(SFT_F);                    break; \
  case 2: ParserInit(0, i); LoadMemory(SFT_D);                    break; \
  case 3: ParserInit(0, i); LoadMemory("012");   LoadFile(SFT_F); break; \
  case 4: ParserInit(0, i);   LoadFile(SFT_F); LoadMemory(SFT_D); break; \
}

void SimpleFragTest(void)
{
  int input;
  int i;
  int errors = 0;

  printf("|--Simple Frag Test--|\n");

    /* Generate the test file */
  GenerateFile(SFT_F, SFT_D);

    /* Test a variety of buffer sizes. 0 will be cause the default buffer size*
     *   to be involked. This will make sure that the order that the tokens   *
     *   are returned in are as specifed.                                     */

  for(input = 0; input < 5; ++input)
  {
    /* Test the inital chunk of the file. It is possible that only BufSize    *
     *   would be read from the file initially. This can cause "2" to be      *
     *   returned instead of "23", since the padding is used to check the     *
     *   tokens at the end of the buffer. This was discovered and corrected in*
     *   Parser v 9.0                                                         */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS


      AddTokenSep("23"   ); /* Make sure that the Search order is preserved   */
      AddTokenSep("2"    ); /* 23 should be returned instead of 2             */
      AddTokenSep("f"    ); /* Make sure the end isn't ignored                */

      errors += TestGrabToken("01");
      errors += TestGrabToken("23");
      errors += TestGrabToken("456789abcde");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Check to make sure the end of the file is handled correctly. IE, make  *
     *   sure that the end of the file is actually read. It is possiable that *
     *   the last few bytes may be declared as padding, and not actually      *
     *   processed.                                                           */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSep("23"   ); /* Make sure that the Search order is preserved   */
      AddTokenSep("2"    ); /* 23 should be returned instead of 2             */
      AddTokenSep("789ab"); /* Just a "big" token. Attempts to mess up the end*/
      AddTokenSep("f"    ); /* Make sure the end isn't ignored                */

      errors += TestGrabToken("01");
      errors += TestGrabToken("23");
      errors += TestGrabToken("456");
      errors += TestGrabToken("789ab");
      errors += TestGrabToken("cde");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Same as above, but ignore the "23" and the "f"                         */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSeparator("23", 0, -1, 1, 0, 0);
      AddTokenSep("2"    ); /* 23 should be returned instead of 2             */
      AddTokenSep("789ab"); /* Just a "big" token. Attempts to mess up the end*/
      AddTokenSeparator("f", 0, -1, 1, 0, 0);

      errors += TestGrabToken("0123456");
      errors += TestGrabToken("789ab");
      errors += TestGrabToken("cdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Same as above, but ignore a larger token.                              */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSeparator("23456", 0, -1, 1, 0, 0);
      AddTokenSep("789ab"); /* Just a "big" token. Attempts to mess up the end*/
      AddTokenSep("f");

      errors += TestGrabToken("0123456");
      errors += TestGrabToken("789ab");
      errors += TestGrabToken("cde");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Remove the "2" and the "f"                                             */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSeparator("2", 0, -1, 0, 0, 0);
      AddTokenSeparator("f", 0, -1, 0, 0, 0);

      errors += TestGrabToken("01");
      errors += TestGrabToken("3456789abcde");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Test a larger token then the actual data. This greately increases      *
     *   padding that is attached to the buffer. Assuming a buffer size of 1, *
     *   the parser read 1 byte from the file at a time until the end of the  *
     *   file is reached, or the buffer and padding is filled. In this        *
     *   situation, the end of file will be reached for the buffer is full.   *
     *   Ensure that this is handled correctly.                               */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSep("0123456789abcdefg");
      AddTokenSep("0");
      AddTokenSep("23");
      AddTokenSep("2");
      AddTokenSep("789ab");
      AddTokenSep("f");

      errors += TestGrabToken("0");
      errors += TestGrabToken("1");
      errors += TestGrabToken("23");
      errors += TestGrabToken("456");
      errors += TestGrabToken("789ab");
      errors += TestGrabToken("cde");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Check to make sure that the entire file can be read in and returned.   *
     *   IE, if a token matches the entire file, that no extra characters or  *
     *   similar will be returned.                                            */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSep("0123456789abcdef");
      AddTokenSep("0");
      AddTokenSep("23");
      AddTokenSep("2");
      AddTokenSep("789ab");
      AddTokenSep("f");

      errors += TestGrabToken("0123456789abcdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Modification of the above. Instead of having a token match the content *
     *   of the file, no token seps match. Again, the entire file should be   *
     *   returned.                                                            */
    for(i = 0; i < 25; ++i)
    {
      SFT_VERBOSE
      SFT_LOAD_DATA
      OPTIONS

      AddTokenSep("g");

      errors += TestGrabToken("0123456789abcdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    /* Modification of above. Insures that the entire file can be discarded   */
    for(i = 0; i < 25; ++i)
    {
      if(Verbose)
        printf("  Buffer Size: %d\n", i);

      SFT_LOAD_DATA
      OPTIONS

      AddTokenSeparator("0123456789abcdef", 0, -1, 0, 0, 0);

      errors += ShouldBeEmpty();

      ParserDeInit();
    }
  }

  RemoveFile(SFT_F);

  if(!errors)
    printf("  Pass\n\n");
  else
    printf("  Fail: %d Errors occured\n\n", errors);
}

/*******************************************************************************
   Function: SeekPeekGrabTest()

Description: Tests Seek(), PeekToken(), and GrabToken() to make sure the
               Parsers state is updated correctly, and the funcitons can
               be used together.
*******************************************************************************/
#define SPGT_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define SPGT_F "SeekPeekGrabTest.txt"
#define SPGT_D "0123456789abcdefghijlkmnopqrstuvwxyz"
#define SPGT_LOAD_DATA                                                     \
switch(input)                                                              \
{                                                                          \
  case 0: ParserInit(SPGT_F, i);                                    break; \
  case 1: ParserInit(0, i);   LoadFile(SPGT_F);                     break; \
  case 2: ParserInit(0, i); LoadMemory(SPGT_D);                     break; \
  case 3: ParserInit(0, i);  LoadMemory("012");   LoadFile(SPGT_F); break; \
  case 4: ParserInit(0, i);   LoadFile(SPGT_F); LoadMemory(SPGT_D); break; \
}

void SeekPeekGrabTest(void)
{
  int i;
  int input;
  int errors = 0;
  GenerateFile(SPGT_F, SPGT_D);

  printf("|--Seek, Peek, and Grab Token Test--|\n");

  for(input = 0; input < 5; ++input)
  {
      /* Generic Peek Test */
    for(i = 0; i < 25; ++i)
    {
      SPGT_VERBOSE
      SPGT_LOAD_DATA
      OPTIONS

      AddTokenSep("3");
      AddTokenSep("z");

      errors += TestPeekToken("012");
      errors += TestPeekToken("012");
      errors += TestGrabToken("012");

      errors += TestPeekToken("3");
      errors += TestPeekToken("3");
      errors += TestGrabToken("3");

      errors += TestPeekToken("456789abcdefghijlkmnopqrstuvwxy");
      errors += TestPeekToken("456789abcdefghijlkmnopqrstuvwxy");
      errors += TestGrabToken("456789abcdefghijlkmnopqrstuvwxy");

      errors += TestPeekToken("z");
      errors += TestPeekToken("z");
      errors += TestGrabToken("z");

      ParserDeInit();
    }

      /* Seek and Peek test */
    for(i = 0; i < 25; ++i)
    {
      SPGT_VERBOSE
      SPGT_LOAD_DATA
      OPTIONS

      AddTokenSep("3");
      AddTokenSep("z");

      errors += SeekValid("3");
      errors += SeekInvalid("3");

      errors += SeekValid("a");
      errors += SeekInvalid("a");

      errors += SeekInvalid("ddd");

      errors += TestPeekToken("bcdefghijlkmnopqrstuvwxy");
      errors += TestPeekToken("bcdefghijlkmnopqrstuvwxy");
      errors += TestGrabToken("bcdefghijlkmnopqrstuvwxy");

      errors += SeekValid("z");   /* Ensure that seek checks the end            */
      errors += SeekInvalid("z");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }
  }

  RemoveFile(SPGT_F);

  if(errors)
    printf("  Fail: %d errors occured.\n\n", errors);
  else
    printf("  Pass\n\n");
}

/*******************************************************************************
   Function: NumberTest()

Description: Checks GrabInt() and GrabFloat() in order to make sure
               they function correctly.
*******************************************************************************/
#define NT_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define NT_F "NumberTest.txt"
#define NT_D "    1     2.34 567 abc          "
#define NT_LOAD_DATA                                                   \
switch(input)                                                          \
{                                                                      \
  case 0: ParserInit(NT_F, i);                                  break; \
  case 1: ParserInit(0, i);   LoadFile(NT_F);                   break; \
  case 2: ParserInit(0, i); LoadMemory(NT_D);                   break; \
  case 3: ParserInit(0, i); LoadMemory("01");   LoadFile(NT_F); break; \
  case 4: ParserInit(0, i);   LoadFile(NT_F); LoadMemory(NT_D); break; \
}

void NumberTest(void)
{
  int i;
  int input;
  int errors = 0;
  GenerateFile(NT_F, NT_D);

  printf("|--Number Test--|\n");

  for(input = 0; input < 5; ++input)
  {
    for(i = 0; i < 25; ++i)
    {
      NT_VERBOSE
      NT_LOAD_DATA
      OPTIONS

      AddTokenSeparator(" ", 0, -1, 0, 0, 0);

      errors += TestGrabInt(1);
      errors += TestGrabInt(2); /* Float to int */
      errors += TestGrabInt(567);
      errors += TestGrabInt(0);  /* Text to int */

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    for(i = 0; i < 25; ++i)
    {
      NT_VERBOSE
      NT_LOAD_DATA
      OPTIONS

      AddTokenSeparator(" ", 0, -1, 0, 0, 0);

      errors += TestGrabFloat(1.0f);   /* int to float */
      errors += TestGrabFloat(2.34f);
      errors += TestGrabFloat(567.0f); /* int to float */
      errors += TestGrabFloat(0.0f);   /* Text to float */

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

    for(i = 0; i < 25; ++i)
    {
      NT_VERBOSE
      NT_LOAD_DATA
      OPTIONS

      AddTokenSeparator(" ", 0, -1, 0, 0, 0);

      errors += TestGrabInt(1);   /* int to float */
      errors += TestGrabFloat(2.34f);
      errors += TestGrabInt((int)567.0f); /* int to float */
      errors += TestGrabToken("abc");   /* Text to float */

      errors += ShouldBeEmpty();

      ParserDeInit();
    }
  }

  RemoveFile(NT_F);

  if(errors)
    printf("  Fail: %d errors occured\n\n", errors);
  else
    printf("  Pass\n\n");
}

/*******************************************************************************
   Function: BinaryNumberTest()

Description: Checks all Binary related functions for errors.
*******************************************************************************/
#define BNT_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define BNT_F "BinaryNumberTest.txt"
#define BNT_LOAD_DATA                                                          \
switch(input)                                                                  \
{                                                                              \
  case 0: ParserInit(BNT_F, i);                                          break;\
  case 1: ParserInit(0, i); LoadFile(BNT_F);                             break;\
  case 2: ParserInit(0, i); LoadMemoryLen(buffer, bufsize);              break;\
  case 3: ParserInit(0, i); LoadMemory("01"); LoadFile(BNT_F);           break;\
  case 4:ParserInit(0, i);LoadFile(BNT_F);LoadMemoryLen(buffer, bufsize);break;\
}

void BinaryNumberTest(void)
{
  int i;
  int input;
  int errors = 0;
  const int bufsize = sizeof(int) + sizeof(float) + 8 + sizeof(float);
  char buffer[bufsize];
  float f;

  i = 100;
  memcpy(buffer, &i, sizeof(int));
  i = sizeof(int);
  f = 200.234f;
  memcpy(buffer + i, &f, sizeof(float));
  i += sizeof(float);
  memcpy(buffer + i, "  asdf  ", 8);
  i += 8;
  f = 123.321f;
  memcpy(buffer + i, &f, sizeof(float));


  FILE *fp = fopen(BNT_F, "wb");
  if(!fp)
  {
    printf("  Error: Could not create BinaryNumberTest.txt.\n");
    return;
  }
  fwrite(buffer, bufsize, 1, fp);
  fclose(fp);

  printf("|--Binary Number Test--|\n");

  for(input = 0; input < 5; ++input)
  {
    for(i = 0; i < 25; ++i)
    {
      BNT_VERBOSE
      BNT_LOAD_DATA
      OPTIONS

      errors += TestGrabBinaryInt(100);
      errors += TestGrabBinaryFloat(200.234f);
      errors += TestGrabChar(' ');
      errors += TestGrabChar(' ');
      errors += TestGrabChar('a');
      errors += TestGrabChar('s');
      errors += TestGrabChar('d');
      errors += TestGrabChar('f');
      errors += TestGrabChar(' ');
      errors += TestGrabChar(' ');
      errors += TestGrabBinaryFloat(123.321f);

        /* Nothing should be left in the buffer */
      errors += TestGrabChar(0);
      errors += TestGrabBinaryInt(0);
      errors += TestGrabBinaryFloat(0.0f);

      errors += ShouldBeEOF();

      ParserDeInit();
    }
  }

  RemoveFile(BNT_F);

  if(errors)
    printf("  Fail: %d errors occured\n\n", errors);
  else
    printf("  Pass\n\n");
}

/*******************************************************************************
   Function: TokenSetTest()

Description: Checks to make sure the SwitchTo paramater functions as desired.
*******************************************************************************/
#define TST_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define TST_F "TokenSetTest.txt"
#define TST_D "0123456789abcdef"
#define TST_LOAD_DATA                                                    \
switch(input)                                                            \
{                                                                        \
  case 0: ParserInit(TST_F, i);                                   break; \
  case 1: ParserInit(0, i);   LoadFile(TST_F);                    break; \
  case 2: ParserInit(0, i); LoadMemory(TST_D);                    break; \
  case 3: ParserInit(0, i); LoadMemory("012");   LoadFile(TST_F); break; \
  case 4: ParserInit(0, i);   LoadFile(TST_F); LoadMemory(TST_D); break; \
}

void TokenSetTest(void)
{
  int input;
  int i;
  int errors = 0;

  printf("|--Token Set Test--|\n");

    /* Generate the test file */
  GenerateFile(TST_F, TST_D);

  for(input = 0; input < 5; ++input)
  {
      /* General test. Make sure the token sets actually switch               */
    for(i = 0; i < 25; ++i)
    {
      TST_VERBOSE
      TST_LOAD_DATA
      OPTIONS

      AddTokenSeparator("234", 0, 1, 0, 0, 0);
      AddTokenSep("5");

      AddTokenSet();
      AddTokenSeparator("789", 0, 0, 0, 0, 0);

      SetTokenSet(0);

      errors += TestGrabToken("01");
      errors += TestGrabToken("56");
      errors += TestGrabToken("abcdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* SwitchTo of -2 test                                                  */
    for(i = 0; i < 25; ++i)
    {
      TST_VERBOSE
      TST_LOAD_DATA
      OPTIONS

      AddTokenSeparator("234", 0,  1, 0, 0, 0);
      AddTokenSep("5");
      AddTokenSep("e");

      AddTokenSet();
      AddTokenSeparator("789", 0, -2, 0, 0, 0);

      SetTokenSet(0);

      errors += TestGrabToken("01");
      errors += TestGrabToken("56");
      errors += TestGrabToken("abcd");
      errors += TestGrabToken("e");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* More complex version of the above.                                   */
    for(i = 0; i < 25; ++i)
    {
      TST_VERBOSE
      TST_LOAD_DATA
      OPTIONS

      AddTokenSeparator("234", 1,  1, 0, 0, 0);
      AddTokenSep("5");
      AddTokenSeparator("ab",  1,  2, 0, 0, 0);

      AddTokenSet();
      AddTokenSeparator("789", 1, -2, 0, 0, 0);
      AddTokenSeparator("d",   1, -2, 0, 0, 0);

      AddTokenSet();
      AddTokenSeparator("c",   1,  1, 0, 0, 0);
      AddTokenSeparator("e",   1, -2, 0, 0, 0);

      SetTokenSet(0);

      errors += TestGrabToken("01");
      errors += TestGrabToken("234");
      errors += TestGrabToken("56");
      errors += TestGrabToken("789");
      errors += TestGrabToken("ab");
      errors += TestGrabToken("c");
      errors += TestGrabToken("d");
      errors += TestGrabToken("e");
      errors += TestGrabToken("f");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }
  }

  RemoveFile(TST_F);

  if(errors)
    printf("  Fail: %d errors occured\n\n", errors);
  else
    printf("  Pass\n\n");
}

/*******************************************************************************
   Function: WildcardTest()

Description: Checks to make sure the SwitchTo paramater functions as desired.
*******************************************************************************/
#define WT_VERBOSE if(Verbose) printf("  Input: %d Buf: %d\n", input, i);
#define WT_F "WildcardTest.txt"
#define WT_D "0123456789abcdef"
#define WT_LOAD_DATA                                                    \
switch(input)                                                            \
{                                                                        \
  case 0: ParserInit(WT_F, i);                                   break; \
  case 1: ParserInit(0, i);   LoadFile(WT_F);                    break; \
  case 2: ParserInit(0, i); LoadMemory(WT_D);                    break; \
  case 3: ParserInit(0, i); LoadMemory("012");   LoadFile(WT_F); break; \
  case 4: ParserInit(0, i);   LoadFile(WT_F); LoadMemory(WT_D); break; \
}

void WildcardTest(void)
{
  int input;
  int i;
  int errors = 0;
  int ret;
  int extrasep ;
  int ign      = 1;
  int usewild = UseWild;

  printf("|--Wildcard Test--|\n");

    /* Generate the test file */
  GenerateFile(WT_F, WT_D);

  for(ign = 0; ign <= 1; ++ign)
  for(extrasep = 0; extrasep <= 1; ++extrasep)
  for(ret = 0; ret <= 1; ++ret)
  for(input = 0; input < 5; ++input)
  {
      /* Wildcard at the front */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      if(extrasep) AddTokenSeparator("23",   ret, -1,   0, 0, 0);
                   AddTokenSeparator("*abc", ret, -1, ign, 0, 0);

      if(ign)
      {
        if(extrasep)
        {
                  errors += TestGrabToken("01");
          if(ret) errors += TestGrabToken("23");
                  errors += TestGrabToken("456789abcdef");
        }
        else
          errors += TestGrabToken("0123456789abcdef");
      }
      else
      if(extrasep)
      {
                errors += TestGrabToken("01");
        if(ret) errors += TestGrabToken("23");
        if(ret) errors += TestGrabToken("456789abc");
                errors += TestGrabToken("def");
      }
      else
      {
        if(ret) errors += TestGrabToken("0123456789abc");
                errors += TestGrabToken("def");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the middle */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("ab*d", ret, -1, ign, 0, 0);

      if(ign)
      {
        errors += TestGrabToken("0123456789abcdef");
      }
      else
      {
                errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abcd");
                errors += TestGrabToken("ef");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the front and middle middle */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      if(extrasep) AddTokenSeparator("789",   ret, -1, ign, 0, 0);
                   AddTokenSeparator("*ab*d", ret, -1, ign, 0, 0);

      if(ign)
      {
        errors += TestGrabToken("0123456789abcdef");
      }
      else
      if(extrasep)
      {
                errors += TestGrabToken("0123456");
        if(ret) errors += TestGrabToken("789");
        if(ret) errors += TestGrabToken("abcd");
                errors += TestGrabToken("ef");
      }
      else
      {
        if(ret) errors += TestGrabToken("0123456789abcd");
                errors += TestGrabToken("ef");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the end */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      if(extrasep) AddTokenSeparator("def", ret, -1, ign, 0, 0);
                   AddTokenSeparator("ab*", ret, -1, ign, 0, 0);

      if(ign)
      {
        errors += TestGrabToken("0123456789abcdef");
      }
      else
      if(extrasep)
      {
                errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abc");
        if(ret) errors += TestGrabToken("def");
      }
      else
      {
                errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abcdef");
      }


      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the front and end */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      if(extrasep) AddTokenSeparator("cde",  ret, -1, ign, 0, 0);
                   AddTokenSeparator("*ab*", ret, -1, ign, 0, 0);

      if(ign)
      {
        errors += TestGrabToken("0123456789abcdef");
      }
      else
      if(extrasep)
      {
        if(ret) errors += TestGrabToken("0123456789ab");
        if(ret) errors += TestGrabToken("cde");
                errors += TestGrabToken("f");
      }
      else
        if(ret) errors += TestGrabToken("0123456789abcdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the middle and end */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("ab*d*", ret, -1, ign, 0, 0);

      if(ign)
      {
        errors += TestGrabToken("0123456789abcdef");
      }
      else
      {
                errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abcdef");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Wildcard at the front, middle, and end */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("*ab*d*", ret, -1, ign, 0, 0);

      if(ret || ign) errors += TestGrabToken("0123456789abcdef");

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Multiple Wildcards back to back */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("*678*",  ret, -1, ign, 0, 0);
      AddTokenSeparator("*ab*d*", ret, -1,   0, 0, 0);
      AddTokenSeparator("*e*f*",  ret, -1, ign, 0, 0);

      if(ign)
      {
        if(ret) errors += TestGrabToken("0123456789abcdef");
        /* If !ret, everything should be discared by "*ab*d*" */
      }
      else
      {
        if(ret) errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abcd");
        if(ret) errors += TestGrabToken("ef");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Variation of the above */
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("*678*", ret, -1, ign, 0, 0);
      AddTokenSeparator("a*c",   ret, -1,   0, 0, 0);
      AddTokenSeparator("*e*f*", ret, -1, ign, 0, 0);

      if(ign & !ret)
      {
        errors += TestGrabToken("0123456789");
        errors += TestGrabToken("def");
      }
      else
      {
        if(ret) errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("abc");
        if(ret) errors += TestGrabToken("def");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }

      /* Test to make sure the token sets are handled with forward searching*/
    for(i = 0; i < 25; ++i)
    {
      WT_VERBOSE
      WT_LOAD_DATA
      OPTIONS
      FORCE_WILD

      AddTokenSeparator("*3*", ret,  1, ign, 0, 0);
      AddTokenSeparator("f",   ret, -1,   0, 0, 0);

      AddTokenSet();
      AddTokenSeparator("a",   ret, -2, ign, 0, 0);

      SetTokenSet(0);

      if(ign)
      {
                errors += TestGrabToken("0123456789abcde");
        if(ret) errors += TestGrabToken("f");
      }
      else
      {
        if(ret) errors += TestGrabToken("0123456789");
        if(ret) errors += TestGrabToken("a");
                errors += TestGrabToken("bcde");
        if(ret) errors += TestGrabToken("f");
      }

      errors += ShouldBeEmpty();

      ParserDeInit();
    }
  }

  RemoveFile(WT_F);

  if(errors)
    printf("  Fail: %d errors occured\n\n", errors);
  else
    printf("  Pass\n\n");

  UseWild = usewild;
}



/******************************************************************************
 *  Start Of Helper Functions                                                 *
 ******************************************************************************/



/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int GenerateFile(const char *file, const char *data)
{
  FILE *fp;

  fp = fopen(file, "w");
  if(!fp)
  {
    printf("  Error: Could not create %s\n", file);
    return 0;
  }
  fwrite(data, strlen(data), 1, fp);
  fclose(fp);

  return 1;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
void RemoveFile(const char *file)
{
  remove(file);
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabTokenL(const char *str, int linenum)
{
  char *buffer = GrabToken();
  int Error = 0;
  if(!buffer)
  {
    printf("  Error:%4d: Expect %s, but nothing was returned\n", linenum, str);
    return 1;
  }

  if(Verbose)
    printf("  |%s|\n", buffer);
  if(strcmp(buffer, str))
  {
    printf("  Error:%4d: %s should have been returned. Recieved |%s|\n",linenum, str, buffer);
    Error = 1;
  }
  free(buffer);

  return Error;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestPeekTokenL(const char *str, int linenum)
{
  char *buffer = PeekToken();
  int Error = 0;
  if(!buffer)
  {
    printf("  Error:%4d: Expect %s, but nothing was returned\n", linenum, str);
    return 1;
  }

  if(Verbose)
    printf("  |%s|\n", buffer);
  if(strcmp(buffer, str))
  {
    printf("  Error:%4d: %s should have been returned. Recieved |%s|\n",linenum, str, buffer);
    Error = 1;
  }
  free(buffer);

  return Error;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int ShouldBeEmptyL(int linenum)
{
  char *buffer = 0;
  int   Error  = 0;

    /* One extra token may have to be read in to set the EOF flag */
  if(ErrorCode() == PARSER_NO_ERROR)
    buffer = GrabToken();

  if(ErrorCode() != PARSER_END_OF_FILE && ErrorCode() != PARSER_COULD_NOT_OPEN_FILE)
  {
    printf("  Error:%4d: Expected End of File. Recieved: %d\n", linenum, ErrorCode());
    PrintErrorCode();
    Error = 1;
  }

  if(buffer)
  {
    printf("  Error:%4d: Recieved |%s| when we shouldn't have recieved anything\n", linenum, buffer);
    free(buffer);
    Error = 1;
  }

  return Error;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int ShouldBeEOFL(int linenum)
{
  if(ErrorCode() != PARSER_END_OF_FILE)
  {
    printf("  Error:%4d: Expected End of File. Recieved: %d\n", linenum, ErrorCode());
    PrintErrorCode();
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int SeekValidL(const char *str, int linenum)
{
  if(!Seek(str))
  {
    printf("  Error:%4d: Expected Valid Seek\n", linenum);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int SeekInvalidL(const char *str, int linenum)
{
  if(Seek(str))
  {
    printf("  Error:%4d: Expected Invalid Seek\n", linenum);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabIntL(int val, int linenum)
{
  int result = GrabInt();
  if(val != result)
  {
    printf("  Error:%4d: %d != %d\n", linenum, val, result);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabFloatL(float val, int linenum)
{
  float result = GrabFloat();
  if(val != result)
  {
    printf("  Error:%4d: %6.2f != %6.2f\n", linenum, val, result);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabBinaryIntL(int val, int linenum)
{
  int result = GrabBinaryInt();
  if(val != result)
  {
    printf("  Error:%4d: %d != %d\n", linenum, val, result);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabBinaryFloatL(float val, int linenum)
{
  float result = GrabBinaryFloat();
  if(val != result)
  {
    printf("  Error:%4d: %6.2f != %6.2f\n", linenum, val, result);
    return 1;
  }

  return 0;
}

/*******************************************************************************
   Function:

Description:

     Inputs:

    Outputs:
*******************************************************************************/
int TestGrabCharL(char val, int linenum)
{
  char result = (char)GrabByte();
  if(val != result)
  {
    printf("  Error:%4d: %d != %d\n", linenum, val, result);
    return 1;
  }

  return 0;
}

#undef AddTokenSep
#undef AddTokenSeparator
int PushTokenSep(const char *sep)
{
  return PushTokenSeparator(sep, 1, -1, 0, 0, 0);
}

int PushTokenSeparator(const char *sep, int a, int b, int c, void *d, void *e)
{
  int i;
  int res;
  int del = 0;

  if(CaseIns){sep = ToUpper(Dup(sep)); del = 1;}

  if(UseWild)
  {
    if(!del)
      sep = Dup(sep);

    for(i = 1; sep[i]; ++i)
      if(sep[i - 1] != '*' && sep[i + 1] != '*' && sep[i + 1])
        ((char *)sep)[i] = '*';
    del = 1;
  }

  res = AddTokenSeparator(sep, (char)a, (char)b, (char)c, (PARSER_CALLBACK)d, e);

  if(del) free((char *)sep);

  return res;
}
